package xyz.zhzby.cyzx.oj.messenger;

import com.alibaba.nacos.common.utils.JacksonUtils;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import xyz.zhzby.cyzx.oj.service.biz.ProblemWebSocketService;

import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * @author zby
 * @date 2022/02/06
 */
@Component
@Slf4j
@ApiModel("应用程序事件监听器. 负责将消息队列中的消息转发至控制器")
public class ApplicationEventListener {

    @ApiModelProperty("在线评测机的列表. Map中的Key表示评测机的用户名. Map中的Value表示对应评测机的信息.")
    private static final Map<String, Map<String, Object>> onlineJudgers = new Hashtable<>();

    @ApiModelProperty("ScheduledExecutorService对象. 用于定期移除离线的评测机.")
    private static ScheduledExecutorService scheduler = null;

    @Autowired
    private ProblemWebSocketService problemWebSocketService;

    public ApplicationEventListener() {
        synchronized (this) {
            if (scheduler == null) {
                final int INITIAL_DELAY = 0;
                final int PERIOD = 30;

                scheduler = Executors.newScheduledThreadPool(1);
                scheduler.scheduleAtFixedRate(
                        () -> {
                            Calendar calendar = Calendar.getInstance();
                            calendar.add(Calendar.MINUTE, -PERIOD);
                            Date heartbeatTimeDeadline = calendar.getTime();

                            for (Iterator<Map.Entry<String, Map<String, Object>>> itr =
                                 onlineJudgers.entrySet().iterator();
                                 itr.hasNext(); ) {
                                Map.Entry<String, Map<String, Object>> entry = itr.next();
                                Date lastHeartbeatTime = (Date) entry.getValue().get("heartbeatTime");

                                if (!lastHeartbeatTime.after(heartbeatTimeDeadline)) {
                                    itr.remove();
                                }
                            }
                        },
                        INITIAL_DELAY,
                        PERIOD,
                        TimeUnit.MINUTES);
            }
        }
    }

    /**
     * @Param [event] [提交记录事件]
     * @Return void []
     * @Author zby
     * @Date 2022/02/06
     * @Description 提交事件的处理器.
     */
    @EventListener
    public void submissionEventHandler(SubmissionEvent event) {
        problemWebSocketService.sendResultMessage(event.getUserId(), event.getPageId(), JacksonUtils.toJson(event));
    }

    /**
     * @Param [event] [评测机心跳事件]
     * @Return void []
     * @Author zby
     * @Date 2022/02/06
     * @Description 处理评测机心跳事件.
     */
    @EventListener
    public void keepAliveEventHandler(KeepAliveEvent event) {
        String judgerUsername = event.getJudgerUsername();
        String judgerDescription = event.getJudgerDescription();
        Date heartbeatTime = event.getHeartbeatTime();

        Map<String, Object> judgerInformation = new HashMap<>();
        judgerInformation.put("description", judgerDescription);
        judgerInformation.put("heartbeatTime", heartbeatTime);

        onlineJudgers.put(judgerUsername, judgerInformation);
    }

    /**
     * @Param [judgerUsername] [评测机的用户名]
     * @Return java.lang.String [评测机的描述信息]
     * @Author zby
     * @Date 2022/02/06
     * @Description 获取评测机的描述信息.
     */
    public String getJudgerDescription(String judgerUsername) {
        String judgerDescription = "[Offline]";

        if (onlineJudgers.containsKey(judgerUsername)) {
            String description = (String) onlineJudgers.get(judgerUsername).get("description");
            judgerDescription = "[Online] " + description;
        }
        return judgerDescription;
    }

    /**
     * @Param [] []
     * @Return long [在线评测机的数量]
     * @Author zby
     * @Date 2022/02/06
     * @Description 获取在线评测机的数量.
     */
    public long getOnlineJudgers() {
        return onlineJudgers.size();
    }

}
